package com.sig.util;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.AbstractDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializer;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.sig.common.util.SpringUtil;
import com.sig.message.GeckoMessage;
import com.sig.message.GeckoMessageManagerAdapter;
import com.sig.util.json.JsonL2S;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Json处理工具
 *
 * @author sig
 * @since 2019/1/30
 */
public final class JsonUtil {
    private static ObjectMapper mapper;
    private static GeckoMessageManagerAdapter geckoMessageManagerAdapter;

    static {
        mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new JsonBeanDeserializerModifier());
        module.setSerializerModifier(new JsonBeanSerializerModifier());
        mapper.registerModule(module);
    }

    private JsonUtil() {
    }

    public static ObjectMapper getObjectMapper() {
        return mapper;
    }

    public static String toString(final Object value) {
        return toString(value, true);
    }

    public static String toString(final Object value, final boolean error) {
        if (value == null)
            return null;
        try {
            return mapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            if (error)
                LogUtil.errorLog(e, e);
            return null;
        }
    }

    public static String toPrettyString(final Object value) {
        return toPrettyString(value, true);
    }

    public static String toPrettyString(final Object value, final boolean error) {
        if (value == null)
            return "{}";
        try {
            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
        } catch (JsonProcessingException e) {
            if (error)
                LogUtil.errorLog(e, e);
            return "{}";
        }
    }

    public static <T> T toObject(final String content, final Class<T> clazz) {
        return toObject(content, clazz, true);
    }

    public static <T> T toObject(final String content, final Class<T> clazz, final boolean error) {
        if (StringUtils.isBlank(content))
            return null;
        try {
            return mapper.readValue(content, clazz);
        } catch (IOException e) {
            if (error)
                LogUtil.errorLog(e, e);
            return null;
        }
    }

    public static <T> List<T> toList(final String content, final Class<T> clazz) {
        return toList(content, clazz, true);
    }

    public static <T> List<T> toList(final String content, final Class<T> clazz, final boolean error) {
        if (StringUtils.isBlank(content))
            return Collections.emptyList();
        try {
            return mapper.readValue(content, TypeFactory.defaultInstance().constructCollectionType(List.class, clazz));
        } catch (IOException e) {
            if (error)
                LogUtil.errorLog(e, e);
            return Collections.emptyList();
        }
    }

    public static <K, V> Map<K, V> toMap(final String content, final Class<K> keyClass, final Class<V> valueClass) {
        return toMap(content, keyClass, valueClass, true);
    }

    public static <K, V> Map<K, V> toMap(final String content, final Class<K> keyClass, final Class<V> valueClass, final boolean error) {
        if (StringUtils.isBlank(content))
            return Collections.emptyMap();
        try {
            return mapper.readValue(content, TypeFactory.defaultInstance().constructMapType(Map.class, keyClass, valueClass));
        } catch (IOException e) {
            if (error)
                LogUtil.errorLog(e, e);
            return Collections.emptyMap();
        }
    }

    public static <T> T toObject(final JsonNode node, final Class<T> valueType) {
        T bean = null;
        try {
            bean = mapper.treeToValue(node, valueType);
        } catch (JsonProcessingException e) {
            LogUtil.errorLog(e, e);
        }
        return bean;
    }

    public static JsonNode toJsonNode(final String content) {
        final JsonFactory factory = mapper.getFactory();
        JsonNode node = null;
        try {
            final JsonParser parser = factory.createParser(content);
            node = mapper.readTree(parser);
        } catch (IOException e) {
            LogUtil.errorLog(e, e);
        }
        return node;
    }

    private static GeckoMessageManagerAdapter geckoMessageManager() {
        if (geckoMessageManagerAdapter == null)
            geckoMessageManagerAdapter = SpringUtil.getBeanOfType(GeckoMessageManagerAdapter.class);
        return geckoMessageManagerAdapter;
    }

    static class JsonBeanDeserializerModifier extends BeanDeserializerModifier {
        JsonBeanDeserializerModifier() {
        }

        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
            return GeckoMessage.class.isAssignableFrom(beanDesc.getBeanClass()) && AbstractDeserializer.class.isAssignableFrom(deserializer.getClass()) ? new GeckoMessageDeserializer() : deserializer;
        }

        class GeckoMessageDeserializer extends StdDeserializer<GeckoMessage> {

            protected GeckoMessageDeserializer() {
                super(GeckoMessage.class);
            }

            @Override
            public GeckoMessage deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
                ObjectNode root = mapper.readTree(jsonParser);
                JsonNode jsonNode = root.get("_id");
                int messageTypeId = jsonNode.asInt();
                Class<? extends GeckoMessage> clazz = geckoMessageManager().getClass(messageTypeId);
                return clazz == null ? null : mapper.treeToValue(root, clazz);
            }
        }
    }

    static class JsonBeanSerializerModifier extends BeanSerializerModifier {
        JsonBeanSerializerModifier() {
        }

        @Override
        public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
            return GeckoMessage.class.isAssignableFrom(beanDesc.getBeanClass()) && BeanSerializer.class.isAssignableFrom(serializer.getClass()) ? new GeckoMessageSerializer((BeanSerializer) serializer) : serializer;
        }

        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            for (BeanPropertyWriter writer : beanProperties) {
                JsonL2S sjs = writer.getAnnotation(JsonL2S.class);
                if (sjs != null)
                    writer.assignSerializer(ToStringSerializer.instance);
            }
            return super.changeProperties(config, beanDesc, beanProperties);
        }

        class GeckoMessageSerializer extends BeanSerializer {

            protected GeckoMessageSerializer(BeanSerializerBase src) {
                super(src);
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException {
                Class<? extends GeckoMessage> clazz = (Class<? extends GeckoMessage>) bean.getClass();
                int messageTypeId = geckoMessageManager().getType(clazz);
                if (messageTypeId > 0)
                    gen.writeNumberField("_id", messageTypeId);
                super.serializeFields(bean, gen, provider);
            }
        }
    }
}
